package edu.pku.sei.sct.primitives;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Matcher;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EReference;

import edu.pku.sei.AMT.Key;
import edu.pku.sei.AMT.LinkConstraint;
import edu.pku.sei.AMT.ObjectVariable;
import edu.pku.sei.AMT.Section;
import edu.pku.sei.ocl.cst.CSTNode;
import edu.pku.sei.ocl.utilities.CacheUtil;
import edu.pku.sei.sct.evaluation.SCTEnvironment;
import edu.pku.sei.sct.util.SectionSet;

public class GetKeyExpressions extends AbstractPrimitive {

	public static final String ID = "GetKeyExpressions";
	
	private CacheUtil keyExprCache = new CacheUtil();
	
	
	public GetKeyExpressions(SCTEnvironment e) {
		super(e);
	}

	/**
	 * arg[0] = variable
	 * arg[1] = List\<Section\>
	 */
	@SuppressWarnings("unchecked")
	@Override
	public Object execute(Object... argv) {
		ObjectVariable v = (ObjectVariable) argv[0];
		List<Section> sects = (List<Section>)argv[1];
		SectionSet sections = new SectionSet(sects);
		List<String> expressions = (List<String>) keyExprCache.get(v, sections);
		
		
		if(expressions==null) {
			EClass type = this.getSctEnvironment().getType(v.getScope(), v.getType());
			List<Key> keys = this.getSctEnvironment().getTransformationEnvironment().getTransformation().getKey(type);
			List<String> allExprs = constructExprs(sections);
			expressions = findKeyExpression(v, keys, allExprs);
			keyExprCache.put(expressions, v, sections);
		}

		System.out.println("get key expressions for "+v+"\n"+expressions);

		return expressions;
	}

	private List<String> constructExprs(SectionSet sections) {
		List<String> allExprs = new ArrayList<String>();
		for(Section sec : sections.getArray()) {
			allExprs.addAll(sec.getExpressions());
			for(Object lo : sec.getLinks()) {
				LinkConstraint l = (LinkConstraint) lo;
				EReference ref = this.getSctEnvironment().getReference(l.getSource().getScope(), l.getSource().getType(), l.getRefName());
				if(ref.isMany()) {
					allExprs.add(l.getSource().getName()+"."+ref.getName()+"->includes("+l.getTarget().getName()+")");
				} else {
					allExprs.add(l.getSource().getName()+"."+ref.getName()+"="+l.getTarget().getName());
				}
				EReference oRef = ref.getEOpposite();
				if(oRef!=null) {
					if(oRef.isMany()) {
						allExprs.add(l.getTarget().getName()+"."+oRef.getName()+"->includes("+l.getSource().getName()+")");
					} else {
						allExprs.add(l.getTarget().getName()+"."+oRef.getName()+"="+l.getSource().getName());
					}
				}
			}
		}
		return allExprs;
	}
	
	

	private List<String> findKeyExpression(ObjectVariable v,
			List<Key> keys, List expressions) {
		
		ArrayList<String> result = new ArrayList<String>();
		ArrayList<String> partialResult = new ArrayList<String>();
		for(Key k : keys) {
			partialResult.clear();
			boolean isFailed = true;
			
			for(Object fo : k.getFeatureNames()) {
				String featureName = (String) fo;
				
				String p1 = "";
				String p2 = "";
				if(featureName.charAt(0)=='!') {
					p1 = "(.)*\\.\\s*"+featureName.substring(1)+"\\s*=\\s*"+v.getName()+"(\\W(.)*)?";
					p2 = "(.)*\\.\\s*"+featureName.substring(1)+"\\s*->\\s*includes\\("+v.getName()+"\\)(.)*";
				} else if(featureName.charAt(0)=='@'){
					p1 = "((.)*\\W)?"+v.getName()+"[.]*(\\s)*\\.(\\s)*"+featureName.substring(1)+"\\s*=(.)*";
					p2 = "((.)*\\W)?"+v.getName()+"[.]*(\\s)*\\.(\\s)*"+featureName.substring(1)+"(\\s)*->(\\s)*includes(.)*";
				}
				
				
				
				for(int i=0;i<expressions.size();i++) {
					String expr = (String)expressions.get(i);
					if(expr.matches(p1) || expr.matches(p2)) {
						partialResult.add(expr);
						isFailed = false;
					}
				}
				if(isFailed) break;
			}
			
			if(isFailed==false) {
				result.addAll(partialResult);
			}
		}
		
		return result;
	}

	@Override
	public String getID() {
		return ID;
	}
}
